/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Graph } from './Graph';
import { Rect } from './Rect';
import { TimeRange } from './RangeRuler';
import { Flag } from './Flag';
import { ns2s, ns2x, randomRgbColor, TimerShaftElement } from '../TimerShaftElement';
import { TraceRow } from '../base/TraceRow';
import { SpApplication } from '../../../SpApplication';
import { Utils } from '../base/Utils';

export enum StType {
  TEMP, //临时的
  PERM, // 永久的
}

export class SlicesTime {
  private _id: string;
  startTime: number = 0;
  endTime: number = 0;
  startNS: number;
  endNS: number;
  color: string = '';
  startX: number;
  endX: number;
  selected: boolean = true;
  hidden: boolean = false;
  text: string = '';
  type: number = StType.PERM; // 默认类型为永久的
  constructor(
    startTime: number = 0,
    endTime: number = 0,
    startNS: number,
    endNS: number,
    startX: number,
    endX: number,
    color: string,
    text: string,
    selected: boolean = true
  ) {
    this._id = Utils.uuid();
    this.startTime = startTime;
    this.endTime = endTime;
    this.startNS = startNS;
    this.endNS = endNS;
    this.color = color;
    this.startX = startX;
    this.endX = endX;
    this.text = text;
    this.selected = selected;
  }

  get id(): string {
    return this._id;
  }
}

const TRIWIDTH: number = 10; // 定义三角形的边长
const TEXT_FONT: string = '12px Microsoft YaHei'; // 文本字体格式
export class SportRuler extends Graph {
  static isMouseInSportRuler = false;
  public flagList: Array<Flag> = [];
  public slicesTimeList: Array<SlicesTime> = [];
  isRangeSelect: boolean = false; //region selection
  private hoverFlag: Flag = new Flag(-1, 0, 0, 0, 0);
  private lineColor: string | null = null;
  private rulerW = 0;
  private _range: TimeRange = {} as TimeRange;
  private readonly notifyHandler:
    | ((hoverFlag: Flag | undefined | null, selectFlag: Flag | undefined | null) => void)
    | undefined;
  private readonly flagClickHandler: ((flag: Flag | undefined | null) => void) | undefined;
  private readonly rangeClickHandler: ((sliceTime: SlicesTime | undefined | null) => void) | undefined;
  private invertedTriangleTime: number | null | undefined = null;
  private slicesTime: {
    startTime: number | null | undefined;
    endTime: number | null | undefined;
    color: string | null;
  } | null = {
      startTime: null,
      endTime: null,
      color: null,
    };
  private timerShaftEL: TimerShaftElement | undefined | null;
  private timeArray: Array<number> = [];
  private countArray: Array<number> = [];
  private durArray: Array<number> = [];
  private _isShow: boolean = true;
  private mouseIn: boolean = false;
  constructor(
    timerShaftEL: TimerShaftElement,
    frame: Rect,
    notifyHandler: (hoverFlag: Flag | undefined | null, selectFlag: Flag | undefined | null) => void,
    flagClickHandler: (flag: Flag | undefined | null) => void,
    rangeClickHandler: (sliceTime: SlicesTime | undefined | null) => void
  ) {
    super(timerShaftEL.canvas, timerShaftEL.ctx!, frame);
    this.notifyHandler = notifyHandler;
    this.flagClickHandler = flagClickHandler;
    this.rangeClickHandler = rangeClickHandler;
    this.timerShaftEL = timerShaftEL;
  }

  get range(): TimeRange {
    return this._range;
  }

  set range(value: TimeRange) {
    this._range = value;
    this.draw();
  }

  set times(timeArray: Array<number>) {
    this.timeArray = timeArray;
  }

  set counts(countArray: Array<number>) {
    this.countArray = countArray;
  }

  set durations(durArray: Array<number>) {
    this.durArray = durArray;
  }

  set showRuler(isShow: boolean) {
    this._isShow = isShow;
  }

  modifyFlagList(flag: Flag | null | undefined): void {
    if (flag) {
      if (flag.hidden) {
        let i = this.flagList.findIndex((it) => it.time === flag.time);
        this.flagList.splice(i, 1);
      } else {
        let i = this.flagList.findIndex((it) => it.time === flag.time);
        this.flagList[i] = flag;
      }
    } else {
      this.flagList.forEach((it) => (it.selected = false));
    }
    this.draw();
  }

  modifySicesTimeList(slicestime: SlicesTime | null | undefined): void {
    if (slicestime) {
      let i = this.slicesTimeList.findIndex((it) => it.id === slicestime.id);
      if (slicestime.hidden) {
        this.slicesTimeList.splice(i, 1);
        let selectionParam = this.timerShaftEL?.selectionMap.get(slicestime.id);
        this.timerShaftEL?.selectionMap.delete(slicestime.id);
        if (selectionParam) {
          this.timerShaftEL?.selectionList.splice(this.timerShaftEL?.selectionList.indexOf(selectionParam), 1);
        }
      } else {
        this.slicesTimeList[i] = slicestime;
      }
    } else {
      this.slicesTimeList.forEach((it) => (it.selected = false));
    }
    this.draw();
  }

  draw(): void {
    this.draBasicsRuler();
    //绘制旗子
    this.flagList.forEach((flagObj: Flag, b) => {
      if (flagObj.time >= this.range.startNS && flagObj.time <= this.range.endNS) {
        flagObj.x = Math.round(
          (this.rulerW * (flagObj.time - this.range.startNS)) / (this.range.endNS - this.range.startNS)
        );
        this.drawFlag(flagObj.x, flagObj.color, flagObj.selected, flagObj.text, flagObj.type);
      }
    });
    !this.hoverFlag.hidden && this.drawFlag(this.hoverFlag.x, this.hoverFlag.color, true, this.hoverFlag.text);
    //If region selection is enabled, the serial number draws a line on the axis to show the length of the box selection
    if (this.isRangeSelect) {
      this.drawRangeSelect();
    }
    if (this.invertedTriangleTime !== null && typeof this.invertedTriangleTime !== undefined) {
      this.drawInvertedTriangle(
        this.invertedTriangleTime!,
        document.querySelector<SpApplication>('sp-application')!.dark ? '#FFFFFF' : '#000000'
      );
    }
    this.slicesTimeList.forEach((slicesTime) => {
      this.drawSlicesMarks(slicesTime);
    });
  }

  draBasicsRuler(): void {
    this.rulerW = this.canvas!.offsetWidth;
    this.context2D.clearRect(this.frame.x, this.frame.y, this.frame.width, this.frame.height + 1);
    this.context2D.beginPath();
    this.lineColor = window.getComputedStyle(this.canvas!, null).getPropertyValue('color');
    this.context2D.lineWidth = 1;
    this.context2D.strokeStyle = this.lineColor; //"#dadada"
    this.context2D.moveTo(this.frame.x, this.frame.y);
    this.context2D.lineTo(this.frame.x + this.frame.width, this.frame.y);
    this.context2D.stroke();
    this.context2D.closePath();
    this.context2D.beginPath();
    this.context2D.strokeStyle = '#999999';
    this.context2D.lineWidth = 3;
    this.context2D.moveTo(this.frame.x, this.frame.y);
    this.context2D.lineTo(this.frame.x, this.frame.y + this.frame.height);
    this.context2D.stroke();
    this.context2D.closePath();
    this.context2D.beginPath();
    this.context2D.strokeStyle = this.lineColor; //"#999999"
    this.context2D.lineWidth = 1;
    this.context2D.fillStyle = '#999999';
    this.context2D.font = '8px sans-serif';
    this.range.xs?.forEach((item, index) => {
      this.context2D.moveTo(item, this.frame.y);
      this.context2D.lineTo(item, this.frame.y + this.frame.height);
      this.context2D.fillText(`${this.range.xsTxt[index]}`, item + 3, this.frame.y + 12);
    });
    this.context2D.stroke();
    this.context2D.closePath();
  }

  private initRangeSelect(): void {
    let range = TraceRow.rangeSelectObject;
    this.context2D.beginPath();
    if (document.querySelector<SpApplication>('sp-application')!.dark) {
      this.context2D.strokeStyle = '#FFF';
      this.context2D.fillStyle = '#FFF';
    } else {
      this.context2D.strokeStyle = '#000';
      this.context2D.fillStyle = '#000';
    }
    let startX = ns2x(range?.startNS || 0, this.range.startNS, this.range.endNS, this.range.totalNS, this.frame);
    let endX = ns2x(range?.endNS || 0, this.range.startNS, this.range.endNS, this.range.totalNS, this.frame);
    let lineWidth = endX - startX;
    let txt = ns2s((range?.endNS || 0) - (range?.startNS || 0));
    this.context2D.moveTo(startX, this.frame.y + 22);
    this.context2D.lineTo(endX, this.frame.y + 22);
    this.context2D.moveTo(startX, this.frame.y + 22 - 5);
    this.context2D.lineTo(startX, this.frame.y + 22 + 5);
    this.context2D.moveTo(endX, this.frame.y + 22 - 5);
    this.context2D.lineTo(endX, this.frame.y + 22 + 5);
    let textWidth = this.context2D.measureText(txt).width;
    if (lineWidth > textWidth) {
      this.context2D.fillText(`${txt}`, startX + (lineWidth - textWidth) / 2, this.frame.y + 20);
    } else {
      if (endX + textWidth >= this.frame.width) {
        this.context2D.fillText(`${txt}`, startX - 5 - textWidth, this.frame.y + 20);
      } else {
        this.context2D.fillText(`${txt}`, endX + 5, this.frame.y + 20);
      }
    }
  }

  drawRangeSelect(): void {
    if (!this._isShow) {
      return;
    }
    this.initRangeSelect();
    if (this.timeArray.length > 0 && TraceRow.rangeSelectObject) {
      // 页面可视框选区域的宽度
      let rangeSelectWidth = TraceRow.rangeSelectObject!.endX! - TraceRow.rangeSelectObject!.startX!;
      // 每段宽度必须大于总数的宽度
      // 10,2+8,2是线的宽度，8是留的空隙，不然很不好看
      // 分section段
      let section = Math.floor(
        rangeSelectWidth / (this.context2D.measureText(String(this.timeArray.length)).width + 10)
      );
      // 最多画二十段
      section < 20 ? (section = section) : (section = 20);
      // 最少一段
      section < 1 ? (section = 1) : (section = section);
      // 框选泳道图并放大左右移动后，框选的部分区域会移出可视区域,
      // TraceRow.rangeSelectObject的开始结束时间仍然是框选时的时间，要和this.range进行比较取可视框选范围的时间
      let startNS;
      let endNS;
      TraceRow.rangeSelectObject!.startNS! > this.range.startNS
        ? (startNS = TraceRow.rangeSelectObject!.startNS!)
        : (startNS = this.range.startNS);
      TraceRow.rangeSelectObject!.endNS! > this.range.endNS
        ? (endNS = this.range.endNS)
        : (endNS = TraceRow.rangeSelectObject!.endNS!);
      // 每一格的时间
      let sectionTime = (endNS - startNS) / section;
      let countArr = new Uint32Array(section);
      let count: number = 0; //某段时间的调用栈数量
      const isEbpf = this.durArray && this.durArray.length > 0;
      const processTimeArray = new Array<Boolean>(this.timeArray.length).fill(false);
      for (let i = 1; i <= section; i++) {
        count = 0;
        for (let j = 0; j < this.timeArray.length; j++) {
          if (processTimeArray[j]) {
            continue;
          }
          let inRange = this.freshInRange(j, startNS, sectionTime, i);
          // 如果该时间小于第一个分割点的时间，计数加1，从而算出一段时间的时间数量
          if (inRange) {
            // nm统计模式则统计每个时间的count
            if (this.countArray && this.countArray[j] > 0) {
              count += this.countArray[j];
            } else {
              count++;
            }
            countArr[i - 1] = count;
            processTimeArray[j] = true;
          } else {
            // 如果遇到大于分割点的时间，就跳过该分割点，计算下一个分割点的时间点数量
            continue;
          }
        }
        this.drawRangeSelectFillText(rangeSelectWidth, section, i, countArr);
      }
    }
    this.context2D.stroke();
    this.context2D.closePath();
  }

  private drawRangeSelectFillText(rangeSelectWidth: number, section: number, i: number, countArr: Uint32Array): void {
    let x = TraceRow.rangeSelectObject!.startX! + (rangeSelectWidth / section) * i;
    if (i !== section) {
      this.context2D.moveTo(x, this.frame.y + 22);
      this.context2D.lineTo(x, this.frame.y + 22 + 5);
    }
    // 每一格的数量的数字宽度
    let countTextWidth = this.context2D.measureText(String(countArr[i - 1])).width;
    // 文本的开始位置 = 框选的开始位置 + 格数 + (一格的宽度 - 文本的宽度) / 2
    let textY =
      TraceRow.rangeSelectObject!.startX! +
      (rangeSelectWidth / section) * (i - 1) +
      (rangeSelectWidth / section - countTextWidth) / 2;
    this.context2D.fillStyle = `#f00`;
    this.context2D.font = `12px sans-serif`;
    this.context2D.fillText(String(countArr[i - 1]), textY, this.frame.y + 22 + 12);
  }

  private freshInRange(j: number, startNS: number, sectionTime: number, i: number): boolean {
    let inRange = false;
    const itemTime = this.timeArray[j];
    // ebpf需要考虑dur
    if (this.durArray && this.durArray.length > 0) {
      const dur = this.durArray[j];
      if (itemTime === this.range.endNS) {
        // 如果时间点刚好和时间轴结束时间一样会导致该时间点没有计数,所以此情况需要的判断条件要多个等号
        inRange =
          itemTime >= startNS + sectionTime * (i - 1) &&
          itemTime <= startNS + sectionTime * i &&
          itemTime >= this.range.startNS &&
          itemTime <= this.range.endNS;
      } else {
        // 判断时间点是否在某时间段内时，一般情况下和左边界相同算在该时间段，和右边界相同算在下一段，
        inRange =
          itemTime + dur >= startNS + sectionTime * (i - 1) &&
          itemTime < startNS + sectionTime * i &&
          itemTime + dur >= this.range.startNS &&
          itemTime < this.range.endNS;
      }
    } else {
      if (itemTime === this.range.endNS) {
        inRange =
          itemTime >= startNS + sectionTime * (i - 1) &&
          itemTime <= startNS + sectionTime * i &&
          itemTime >= this.range.startNS &&
          itemTime <= this.range.endNS;
      } else {
        inRange =
          itemTime >= startNS + sectionTime * (i - 1) &&
          itemTime < startNS + sectionTime * i &&
          itemTime >= this.range.startNS &&
          itemTime < this.range.endNS;
      }
    }
    return inRange;
  }

  drawTriangle(time: number, type: string): unknown {
    let num;
    if (time !== null && typeof time !== undefined) {
      let i = this.flagList.findIndex((it) => it.time === time);
      if (type === 'triangle') {
        let triangle = this.flagList.findIndex((it) => it.type === type);
        if (i !== -1) {
          if (triangle !== -1) {
            this.flagList[i].type === '' ? this.flagList.splice(triangle, 1) : '';
          }
        } else {
          if (triangle === -1) {
            this.flagList.forEach((it) => (it.selected = false));
            this.flagList.push(new Flag(0, 125, 18, 18, time, randomRgbColor(), '', true, 'triangle'));
          } else {
            this.flagList.forEach((it) => (it.selected = false));
            this.flagList[triangle].time = time;
            this.flagList[triangle].selected = true;
          }
        }
      } else if (type === 'square') {
        if (i !== -1) {
          this.flagList[i].type = '';
        } else {
          let triangle = this.flagList.findIndex((it) => it.type === 'triangle');
          if (triangle !== -1) {
            this.flagList[triangle].type = '';
            this.draw();
            this.flagChangeHandler('1');
            num = this.flagList[triangle].time;
          }
        }
      } else if (type === 'inverted') {
        this.invertedTriangleTime = time;
      }
      this.draw();
      this.flagChangeHandler('2');
    }
    return num;
  }

  flagChangeHandler(from?: string): void {
    this.notifyHandler &&
      this.notifyHandler(
        !this.hoverFlag.hidden ? this.hoverFlag : null,
        this.flagList.find((it) => it.selected) || null
      );
  }

  removeTriangle(type: string): void {
    if (type === 'inverted') {
      if (this.invertedTriangleTime !== null) {
        this.flagChangeHandler('3');
      }
      this.invertedTriangleTime = null;
    } else {
      this.flagChangeHandler('3');
    }
    this.draw();
  }

  drawInvertedTriangle(time: number, color: string = '#000000'): void {
    if (time !== null && typeof time !== undefined) {
      let x = Math.round((this.rulerW * (time - this.range.startNS)) / (this.range.endNS - this.range.startNS));
      this.context2D.beginPath();
      this.context2D.fillStyle = color;
      this.context2D.strokeStyle = color;
      // ----------------修改小倒三角位置的绘制---------------------
      if (sessionStorage.getItem('expand') === 'true') {
        //展开
        this.context2D.moveTo(x - 3, 141);
        this.context2D.lineTo(x + 3, 141);
        this.context2D.lineTo(x, 145);
      } else if (sessionStorage.getItem('expand') === 'false') {
        this.context2D.moveTo(x - 3, 141 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 3, 141 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x, 145 - Number(sessionStorage.getItem('foldHeight')));
      }
      this.context2D.fill();
      this.context2D.closePath();
      this.context2D.stroke();
    }
  }

  setSlicesMark(
    startTime: number | null = null,
    endTime: number | null = null,
    shiftKey: boolean | null = null
  ): SlicesTime | null {
    let findSlicesTime = this.slicesTimeList.find((it) => it.startTime === startTime && it.endTime === endTime);
    if (findSlicesTime && this.slicesTimeList.length > 0) {
      return null;
    } else {
      let newSlicestime: SlicesTime | null = null;
      if (startTime !== null && typeof startTime !== undefined && endTime !== null && typeof endTime !== undefined) {
        this.slicesTime = {
          startTime: startTime <= endTime ? startTime : endTime,
          endTime: startTime <= endTime ? endTime : startTime,
          color: null,
        };
        let startX = Math.round(
          (this.rulerW * (startTime - this.range.startNS)) / (this.range.endNS - this.range.startNS)
        );
        let endX = Math.round((this.rulerW * (endTime - this.range.startNS)) / (this.range.endNS - this.range.startNS));
        this.slicesTime.color = randomRgbColor() || '#ff0000';
        let text = '';
        newSlicestime = new SlicesTime(
          this.slicesTime.startTime || 0,
          this.slicesTime.endTime || 0,
          this.range.startNS,
          this.range.endNS,
          startX,
          endX,
          this.slicesTime.color,
          text,
          true
        );
        if (!shiftKey) {
          this.clearTempSlicesTime(); // 清除临时对象
          // 如果没有按下shift键，则把当前slicestime对象的类型设为临时类型。
          newSlicestime.type = StType.TEMP;
        }
        this.slicesTimeList.forEach((slicestime) => (slicestime.selected = false));
        newSlicestime.selected = true;
        this.slicesTimeList.push(newSlicestime);
      } else {
        this.clearTempSlicesTime(); // 清除临时对象
        this.slicesTime = { startTime: null, endTime: null, color: null };
      }
      this.range.slicesTime = this.slicesTime;
      this.draw();
      this.timerShaftEL?.render();
      return newSlicestime;
    }
  }

  // 清除临时对象
  clearTempSlicesTime(): void {
    // 清除以前放入的临时对象
    this.slicesTimeList.forEach((slicestime, index) => {
      slicestime.selected = false;
      if (slicestime.type === StType.TEMP) {
        this.slicesTimeList.splice(index, 1);
        let selectionParam = this.timerShaftEL?.selectionMap.get(slicestime.id);
        if (selectionParam && selectionParam !== undefined) {
          this.timerShaftEL?.selectionList.splice(this.timerShaftEL?.selectionList.indexOf(selectionParam), 1);
          this.timerShaftEL?.selectionMap.delete(slicestime.id);
        }
      }
    });
  }

  clearHoverFlag(): void {
    this.hoverFlag.hidden = true;
  }

  showHoverFlag(): void {
    this.hoverFlag.hidden = false;
  }

  private drawSlicesTimeText(slicesTime: SlicesTime, startX: number, endX: number): number[] {
    this.context2D.beginPath();
    this.context2D.strokeStyle = slicesTime.color;
    this.context2D.fillStyle = slicesTime.color;
    this.range.slicesTime.color = slicesTime.color; //紫色
    // ---------------------------------------修改标记绘制位置-------------------------
    if (sessionStorage.getItem('expand') === 'true') {
      //展开
      this.context2D.moveTo(startX + TRIWIDTH, 132);
      this.context2D.lineTo(startX, 142);
      this.context2D.lineTo(startX, 132);
      this.context2D.lineTo(startX + TRIWIDTH, 132);

      this.context2D.lineTo(endX - TRIWIDTH, 132);
      this.context2D.lineTo(endX, 132);
      this.context2D.lineTo(endX, 142);
      this.context2D.lineTo(endX - TRIWIDTH, 132);
    } else if (sessionStorage.getItem('expand') === 'false') {
      this.context2D.moveTo(startX + TRIWIDTH, 132 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(startX, 142 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(startX, 132 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(startX + TRIWIDTH, 132 - Number(sessionStorage.getItem('foldHeight')));

      this.context2D.lineTo(endX - TRIWIDTH, 132 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(endX, 132 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(endX, 142 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(endX - TRIWIDTH, 132 - Number(sessionStorage.getItem('foldHeight')));
    }
    this.context2D.closePath();
    slicesTime.selected && this.context2D.fill();
    this.context2D.stroke();
    this.context2D.beginPath();
    if (document.querySelector<SpApplication>('sp-application')!.dark) {
      this.context2D.strokeStyle = '#FFF';
      this.context2D.fillStyle = '#FFF';
    } else {
      this.context2D.strokeStyle = '#000';
      this.context2D.fillStyle = '#000';
    }
    let lineWidth = endX - startX;
    let txt = ns2s((slicesTime.endTime || 0) - (slicesTime.startTime || 0));
    this.context2D.moveTo(startX, this.frame.y + 22);
    this.context2D.lineTo(endX, this.frame.y + 22);
    this.context2D.moveTo(startX, this.frame.y + 22 - 5);
    this.context2D.lineTo(startX, this.frame.y + 22 + 5);
    this.context2D.moveTo(endX, this.frame.y + 22 - 5);
    this.context2D.lineTo(endX, this.frame.y + 22 + 5);
    let txtWidth = this.context2D.measureText(txt).width;
    this.context2D.fillStyle = '#FFF'; //为了解决文字重叠问题。在时间刻度的文字下面绘制一个小方块
    this.context2D.fillRect(startX + (lineWidth - txtWidth) / 2, this.frame.y + 10, txtWidth + 2, 10);
    this.context2D.fillStyle = 'black';
    if (lineWidth > txtWidth) {
      this.context2D.fillText(`${txt}`, startX + (lineWidth - txtWidth) / 2, this.frame.y + 20);
    } else {
      if (endX + txtWidth >= this.frame.width) {
        this.context2D.fillText(`${txt}`, startX - 5 - txtWidth, this.frame.y + 20);
      } else {
        this.context2D.fillText(`${txt}`, endX + 5, this.frame.y + 20);
      }
    }
    this.context2D.stroke();
    this.context2D.closePath();
    return [lineWidth, txtWidth];
  }

  drawSlicesMarks(slicesTime: SlicesTime): void {
    if (
      slicesTime.startTime !== null &&
      typeof slicesTime.startTime !== undefined &&
      slicesTime.endTime !== null &&
      typeof slicesTime.endTime !== undefined
    ) {
      let startX = Math.round(
        (this.rulerW * (slicesTime.startTime - this.range.startNS)) / (this.range.endNS - this.range.startNS)
      );
      let endX = Math.round(
        (this.rulerW * (slicesTime.endTime - this.range.startNS)) / (this.range.endNS - this.range.startNS)
      );
      // 放大、缩小、左右移动之后重置小三角的x轴坐标
      slicesTime.startX = startX;
      slicesTime.endX = endX;
      let [lineWidth, txtWidth] = this.drawSlicesTimeText(slicesTime, startX, endX);
      // 画框选的备注文字---begin-----------------
      let text = slicesTime.text;
      if (text) {
        this.context2D.beginPath();
        if (document.querySelector<SpApplication>('sp-application')!.dark) {
          this.context2D.strokeStyle = '#FFF';
          this.context2D.fillStyle = '#FFF';
        } else {
          this.context2D.strokeStyle = '#000';
          this.context2D.fillStyle = '#000';
        }

        let textWidth = this.context2D.measureText(text).width;
        if (textWidth > 0) {
          this.context2D.fillStyle = 'black';
          this.context2D.font = TEXT_FONT;
          if (lineWidth > txtWidth) {
            this.context2D.fillText(`${text}`, startX + (lineWidth - textWidth) / 2, this.frame.y + 43);
          } else {
            if (endX + textWidth >= this.frame.width) {
              this.context2D.fillText(`${text}`, startX - 5 - textWidth, this.frame.y + 43);
            } else {
              this.context2D.fillText(`${text}`, endX + 5, this.frame.y + 43);
            }
          }
        }
        this.context2D.stroke();
        this.context2D.closePath();
      }
      // 画框选的备注文字---end-----------------
    }
  }

  //绘制旗子
  drawFlag(
    x: number,
    color: string = '#999999',
    isFill: boolean = false,
    textStr: string = '',
    type: string = ''
  ): void {
    if (x < 0) {
      return;
    }
    this.context2D.beginPath();
    this.context2D.fillStyle = color;
    this.context2D.strokeStyle = color;
    // ------------------修改旗子位置----------------------------
    if (sessionStorage.getItem('expand') === 'true') {
      this.context2D.moveTo(x, 125);
      if (type === 'triangle') {
        this.context2D.lineTo(x + 15, 131);
      } else {
        this.context2D.lineTo(x + 10, 125);
        this.context2D.lineTo(x + 10, 127);
        this.context2D.lineTo(x + 18, 127);
        this.context2D.lineTo(x + 18, 137);
        this.context2D.lineTo(x + 10, 137);
        this.context2D.lineTo(x + 10, 135);
      }
      this.context2D.lineTo(x + 2, 135);
      this.context2D.lineTo(x + 2, 142);
      this.context2D.lineTo(x, 142);
    } else {
      this.context2D.moveTo(x, 125 - Number(sessionStorage.getItem('foldHeight')));
      if (type === 'triangle') {
        this.context2D.lineTo(x + 15, 131 - Number(sessionStorage.getItem('foldHeight')));
      } else {
        this.context2D.lineTo(x + 10, 125 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 10, 127 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 18, 127 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 18, 137 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 10, 137 - Number(sessionStorage.getItem('foldHeight')));
        this.context2D.lineTo(x + 10, 135 - Number(sessionStorage.getItem('foldHeight')));
      }
      this.context2D.lineTo(x + 2, 135 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(x + 2, 142 - Number(sessionStorage.getItem('foldHeight')));
      this.context2D.lineTo(x, 142 - Number(sessionStorage.getItem('foldHeight')));
    }
    this.context2D.closePath();
    isFill && this.context2D.fill();
    this.context2D.stroke();
    if (textStr !== '') {
      this.context2D.font = TEXT_FONT;
      const { width } = this.context2D.measureText(textStr);
      this.context2D.fillStyle = 'rgba(255, 255, 255, 0.8)'; //
      // -------------------修改旗子上的字位置-----------------------
      if (sessionStorage.getItem('expand') === 'true') {
        this.context2D.fillRect(x + 21, 132, width + 4, 12);
        this.context2D.fillStyle = 'black';
        this.context2D.fillText(textStr, x + 23, 142);
      } else {
        this.context2D.fillRect(x + 21, 132 - Number(sessionStorage.getItem('foldHeight')), width + 4, 12);
        this.context2D.fillStyle = 'black';
        this.context2D.fillText(textStr, x + 23, 142 - Number(sessionStorage.getItem('foldHeight')));
      }
      this.context2D.stroke();
    }
  }

  /**
   * 查找鼠标所在位置是否存在"帽子"对象，为了操作方便，框选时把三角形的边长宽度左右各加一个像素。
   * @param x 水平坐标值
   * @returns
   */
  findSlicesTime(x: number, y: number): SlicesTime | null {
    // --------------------------修改旗子和标记的小三角重叠时的情况-------------------
    let slicestime;
    if (sessionStorage.getItem('expand') === 'false') {
      //折叠
      slicestime = this.slicesTimeList.find((slicesTime) => {
        return (
          ((x >= slicesTime.startX - 1 && x <= slicesTime.startX + TRIWIDTH + 1) || // 选中了帽子的左边三角形区域
            (x >= slicesTime.endX - TRIWIDTH - 1 && x <= slicesTime.endX + 1)) && // 选中了帽子的右边三角形区域
          y >= 132 - Number(sessionStorage.getItem('foldHeight')) &&
          y <= 142 - Number(sessionStorage.getItem('foldHeight'))
        );
      });
    } else if (sessionStorage.getItem('expand') === 'true') {
      slicestime = this.slicesTimeList.find((slicesTime) => {
        return (
          ((x >= slicesTime.startX - 1 && x <= slicesTime.startX + TRIWIDTH + 1) || // 选中了帽子的左边三角形区域
            (x >= slicesTime.endX - TRIWIDTH - 1 && x <= slicesTime.endX + 1)) && // 选中了帽子的右边三角形区域
          y >= 132 &&
          y <= 142
        );
      });
    }
    if (!slicestime) {
      return null;
    }
    return slicestime;
  }

  mouseUp(ev: MouseEvent): void {
    if (this.edgeDetection(ev)) {
      let x = ev.offsetX - (this.canvas?.offsetLeft || 0); // 鼠标点击的x轴坐标
      let y = ev.offsetY; // 鼠标点击的y轴坐标
      let findSlicestime = this.findSlicesTime(x, y); // 查找帽子
      if (findSlicestime) {
        // 如果找到帽子，则选中帽子。
        this.slicesTimeList.forEach((slicestime) => (slicestime.selected = false));
        findSlicestime.selected = true;
        this.rangeClickHandler && this.rangeClickHandler(findSlicestime);
      } else {
        // 如果没有找到帽子，则绘制旗子，此处避免旗子和帽子重叠。
        // 查找旗子
        let findFlag = this.flagList.find(
          (it) => (x >= it.x && x <= it.x + 18 && it.type !== 'triangle') || (x === it.x && it.type === 'triangle')
        );
        this.flagList.forEach((it) => (it.selected = false));
        if (findFlag) {
          findFlag.selected = true;
        } else {
          let flagAtRulerTime = Math.round(((this.range.endNS - this.range.startNS) * x) / this.rulerW);
          let flag = new Flag(x, 125, 18, 18, flagAtRulerTime + this.range.startNS, randomRgbColor(), '', true, '');
          this.flagList.push(flag);
        }
        this.flagClickHandler && this.flagClickHandler(this.flagList.find((it) => it.selected)); // 绘制旗子
      }
    }
  }

  mouseMove(ev: MouseEvent): void {
    if (this.edgeDetection(ev)) {
      this.mouseIn = true;
      let x = ev.offsetX - (this.canvas?.offsetLeft || 0);
      let flg = this.flagList.find((it) => x >= it.x && x <= it.x + 18);
      if (flg) {
        this.hoverFlag.hidden = true;
      } else {
        this.hoverFlag.hidden = false;
        this.hoverFlag.x = x;
        this.hoverFlag.color = '#999999';
      }
      this.flagChangeHandler('4');
      this.draw();
    } else {
      this.hoverFlag.hidden = true;
    }

  }

  mouseOut(ev: MouseEvent): void {
    if (!this.hoverFlag.hidden) {
      this.hoverFlag.hidden = true;
      if (this.mouseIn) {
        this.flagChangeHandler('5');
      }
    }
    if (this.mouseIn) {
      this.mouseIn = false;
      this.draw();
    }
  }

  edgeDetection(ev: MouseEvent): boolean {
    let x = ev.offsetX - (this.canvas?.offsetLeft || 0);
    let y = ev.offsetY - (this.canvas?.offsetTop || 0);
    SportRuler.isMouseInSportRuler =
      x > 0 &&
      x < this.canvas!.offsetWidth &&
      ev.offsetY - this.frame.y > 20 &&
      ev.offsetY - this.frame.y < this.frame.height;
    return SportRuler.isMouseInSportRuler;
  }
}
